package no.uib.hplims.views.search;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.vaadin.appfoundation.persistence.facade.FacadeFactory;
import no.uib.hplims.MyVaadinApplication;
import no.uib.hplims.models.Peptide;
import no.uib.hplims.tools.Icons;
import no.uib.hplims.views.AbstractParentTab;
import no.uib.hplims.views.experiments.AddToExperimentWindow;
import no.uib.hplims.views.peptides.PeptideInformationView;

import com.vaadin.data.Property.ValueChangeEvent;
import com.vaadin.data.Property.ValueChangeListener;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.terminal.ExternalResource;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Button.ClickListener;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.Table;
import com.vaadin.ui.Window;
import com.vaadin.ui.Table.ColumnGenerator;
import com.vaadin.ui.themes.BaseTheme;
import com.vaadin.ui.themes.Runo;
import com.vaadin.ui.TextField;
import com.vaadin.ui.VerticalLayout;

public class AllPeptidesView extends AbstractParentTab {

	private static final long serialVersionUID = 3663129064762233056L;

	private static final String SEARCH_TYPE_SEQUENCE = "Sequence";
	private static final String SEARCH_TYPE_ACCNUM = "UniProt Accession #";
	private static final String SEARCH_TYPE_NAME = "Peptide name";

	private static String caption = "Search";
	private Table peptideTable = null;
	private BeanItemContainer<Peptide> peptideContainer = null;
	private TextField searchField = new TextField();
	private Label info = new Label();
	private Button searchButton = new Button("Search");
	private NativeSelect searchType;
	private Button addToExperimentButton = new Button("Add to experiment");
	private VerticalLayout peptideInformationArea;

	private final ArrayList<Object> visibleColumnIds = new ArrayList<Object>();
	private final ArrayList<String> visibleColumnLabels = new ArrayList<String>();

	public AllPeptidesView() {
		super(caption, Icons.searchTabIcon16);

		init();
	}

	public void activated(Object... params) {

	}

	public void deactivated(Object... params) {

	}

	@Override
	protected void init() {
		VerticalLayout tab = getMainTab();
		tab.setSpacing(true);

		searchButton.setIcon(Icons.searchTabIcon16);
		searchButton.addListener(clickListener);
		searchField.setColumns(25);

		HorizontalLayout hl = new HorizontalLayout();
		hl.setSpacing(true);
		hl.addComponent(searchField);
		hl.addComponent(searchButton);
		hl.setComponentAlignment(searchField, Alignment.MIDDLE_LEFT);
		hl.setComponentAlignment(searchButton, Alignment.MIDDLE_LEFT);

		searchType = new NativeSelect();
		searchType.addItem(SEARCH_TYPE_SEQUENCE);
		searchType.addItem(SEARCH_TYPE_NAME);
		searchType.addItem(SEARCH_TYPE_ACCNUM);
		searchType.select(SEARCH_TYPE_SEQUENCE);
		searchType.setNullSelectionAllowed(false);
		searchType.setImmediate(true);

		hl.addComponent(searchType);

		HorizontalLayout hl2 = new HorizontalLayout();
		hl2.setSpacing(true);
		addToExperimentButton.setEnabled(false);
		addToExperimentButton.addListener(clickListener);
		hl2.addComponent(addToExperimentButton);
		hl2.addComponent(info);
		hl2.setComponentAlignment(info, Alignment.MIDDLE_LEFT);

		tab.addComponent(hl);
		tab.addComponent(hl2);
		Table t = getPeptideTable();
		peptideTable = t;
		tab.addComponent(peptideTable);

	}

	private Table getPeptideTable() {
		Table peptideTable = new Table();
		peptideTable.setImmediate(true);
		peptideTable.setSizeFull();
		peptideTable.setStyleName(Runo.TABLE_SMALL);
		peptideTable.setSelectable(true);
		peptideTable.setMultiSelect(true);

		peptideTable.setContainerDataSource(getBeanItemContainer());

		peptideTable.setColumnExpandRatio("belongsToOrder", 0);
		peptideTable.setColumnExpandRatio("uniProtAccession", 0);
		peptideTable.setColumnExpandRatio("peptideName", 1);
		peptideTable.setColumnExpandRatio("peptideSequence", 1);

		peptideTable.setColumnAlignment("uniProtAccession", Table.ALIGN_CENTER);

		peptideTable.addGeneratedColumn("uniProtAccession",
				new ColumnGenerator() {
					private static final long serialVersionUID = -1666675041355704615L;

					public Object generateCell(Table source, Object itemId,
							Object columnId) {
						Peptide p = (Peptide) itemId;
						Link uniProtLink = new Link(p.getUniProtAccession(),
								new ExternalResource(
										"http://www.uniprot.org/uniprot/"
												+ p.getUniProtAccession()));
						// Open the URL in a new window/tab
						uniProtLink.setTargetName("_blank");
						return uniProtLink;
					}
				});
		peptideTable.addGeneratedColumn("belongsToOrder", orderColumnGenerator);

		peptideTable.addListener(valueChangeListener);

		setUpColumns(peptideTable);

		return peptideTable;
	}

	@Override
	public String toString() {
		return caption;
	}

	private void setUpColumns(Table peptideTable) {
		if (peptideTable != null) {
			// Set up columns
			visibleColumnIds.add("uniProtAccession");
			visibleColumnIds.add("belongsToOrder");
			visibleColumnIds.add("peptideName");
			visibleColumnIds.add("peptideSequence");
			visibleColumnIds.add("sequenceLength");
			visibleColumnIds.add("numBlastHits");

			// Add column labels
			visibleColumnLabels.add("Accession #");
			visibleColumnLabels.add("Order");
			visibleColumnLabels.add("Peptide name");
			visibleColumnLabels.add("Sequence");
			visibleColumnLabels.add("Length");
			visibleColumnLabels.add("BLAST hits");

			peptideTable.setVisibleColumns(visibleColumnIds.toArray());
			peptideTable.setColumnHeaders(visibleColumnLabels
					.toArray(new String[0]));
		}
	}

	private BeanItemContainer<Peptide> getBeanItemContainer() {
		if (peptideContainer == null) {
			peptideContainer = new BeanItemContainer<Peptide>(Peptide.class);
		}
		return peptideContainer;
	}

	private ClickListener clickListener = new ClickListener() {
		private static final long serialVersionUID = 234913389507456673L;

		public void buttonClick(ClickEvent event) {
			if (event.getButton().equals(searchButton)) {
				
				String queryArray[] = searchField.getValue().toString().split("\\+");
				List<Peptide> allResults = new ArrayList<Peptide>();
				
				long st = System.currentTimeMillis();
				for (int i = 0; i < queryArray.length; i ++) {
					String query = "%" + queryArray[i].trim() + "%";
	
					String queryString = buildQuery((String) searchType.getValue());
					Map<String, Object> parameters = new HashMap<String, Object>();
					parameters.put("query", query);
					
					List<Peptide> results = FacadeFactory.getFacade().list(
							queryString, parameters);
					
					
					allResults.addAll(results);
				}
				long time = (System.currentTimeMillis() - st);
				
				if (peptideTable == null) {
					peptideTable = getPeptideTable();
				} else {
					peptideTable.removeAllItems();
				}
				
				getBeanItemContainer().addAll(allResults);

				info.setValue("Found " + allResults.size() + " docs in " + time
						+ "ms");
				
			} else if (event.getButton().equals(addToExperimentButton)) {
				
				addToExperimentButtonClick();
				
			}
		}
	};

	private String buildQuery(String searchType) {

		String pre = "SELECT p FROM Peptide AS p WHERE ";
		String where = "";

		if (searchType == SEARCH_TYPE_SEQUENCE) {
			where = "p.peptideSequence LIKE :query";
		} else if (searchType == SEARCH_TYPE_ACCNUM) {
			where = "p.uniProtAccession LIKE :query";
		} else if (searchType == SEARCH_TYPE_NAME) {
			where = "p.peptideName LIKE :query";
		}

		return pre + where;
	}

	private ColumnGenerator orderColumnGenerator = new ColumnGenerator() {

		private static final long serialVersionUID = 1314470366839796655L;

		public Object generateCell(Table source, Object itemId, Object columnId) {

			final Peptide p = (Peptide) itemId;

			if (p.getBelongsToOrder() != null) {
				Button orderButton = new Button(p.getBelongsToOrder()
						.getOrderTitle());
				orderButton.setStyleName(BaseTheme.BUTTON_LINK);
				orderButton.addListener(new Button.ClickListener() {
					private static final long serialVersionUID = 8701816885465352359L;

					public void buttonClick(ClickEvent event) {
						if (p.getBelongsToOrder() != null) {
							MyVaadinApplication.getInstance().getController()
									.showOrder(p.getBelongsToOrder());
						}
					}
				});
				return orderButton;
			}
			return new Label("No order connected");
		}
	};

	private ValueChangeListener valueChangeListener = new ValueChangeListener() {

		private static final long serialVersionUID = 2983012430219450485L;

		public void valueChange(ValueChangeEvent event) {

			Object selection = event.getProperty().getValue();

			if (selection == null) {
				return;
			}

			ArrayList<Peptide> selectionIndexes = new ArrayList<Peptide>();

			if (selection instanceof Peptide) {
				selectionIndexes.add((Peptide) selection);
			} else if (selection instanceof Collection) {
				selectionIndexes.addAll((Collection<Peptide>) selection);
			}
			
			if (selectionIndexes.size() > 0) {
				addToExperimentButton.setEnabled(true);
			} else {
				addToExperimentButton.setEnabled(false);
			}

			if (selectionIndexes.size() == 1) {
				Peptide p = selectionIndexes.get(0);

				if (p != null) {
					if (peptideInformationArea == null) {
						peptideInformationArea = new PeptideInformationView(p,
								null);
						getMainTab().addComponent(peptideInformationArea);
					} else {
						PeptideInformationView piv = new PeptideInformationView(
								p, null);
						getMainTab().replaceComponent(peptideInformationArea,
								piv);
						peptideInformationArea = piv;
					}
				} else {
					getMainTab().removeComponent(peptideInformationArea);
					peptideInformationArea = null;
				}
			} else {
				if (peptideInformationArea != null) {
					getMainTab().removeComponent(peptideInformationArea);
					peptideInformationArea = null;
				}
			}
		}
	};

	private void addToExperimentButtonClick() {
		final Object selection = peptideTable.getValue();
		Collection<Peptide> selectionIndexes = null;
		if (selection == null) {
			return;
		}
		if (selection instanceof Peptide) {
			selectionIndexes = new ArrayList<Peptide>();
			selectionIndexes.add((Peptide) selection);
		} else if (selection instanceof Collection) {
			selectionIndexes = (Collection<Peptide>) selection;
		}

		if (selectionIndexes != null) {
			Window window = new AddToExperimentWindow(selectionIndexes);

			getWindow().addWindow(window);
		}
	}
}
